package com.gameley.dmgtpf.common.base;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.*;

import java.lang.reflect.Method;
import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

// import org.springframework.cloud.context.config.annotation.RefreshScope;

// @PropertySource(value = {"classpath:/config/redis.properties"})
// @Configuration
// @EnableCaching
// @RefreshScope
public class RedisConfig extends CachingConfigurerSupport
{
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.timeout}")
    private int timeout;
    @Value("${spring.redis.password}")
    private String password;
    @Value("${spring.redis.pool.max-active}")
    private int maxActive;
    @Value("${spring.redis.pool.max-wait}")
    private int maxWait;
    @Value("${spring.redis.pool.max-idle}")
    private int maxIdle;
    @Value("${spring.redis.pool.min-idle}")
    private int minIdle;
    
    // @RefreshScope
    @Bean
    public KeyGenerator wiselyKeyGenerator()
    {
        return new KeyGenerator()
        {
            @Override
            public Object generate(Object target, Method method, Object... params)
            {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params)
                {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }
    
    // @RefreshScope
    @Bean
    public JedisConnectionFactory redisConnectionFactory()
    {
        JedisConnectionFactory factory = new JedisConnectionFactory();
        factory.setHostName(host);
        factory.setPort(port);
        factory.setTimeout(timeout); // 设置连接超时时间
        factory.setPassword(password);
        factory.getPoolConfig().setMaxIdle(maxIdle);
        factory.getPoolConfig().setMinIdle(minIdle);
        factory.getPoolConfig().setMaxTotal(maxActive);
        factory.getPoolConfig().setMaxWaitMillis(maxWait);
        return factory;
    }
    
    /**
     * *********************************************************** <br>
     * *说明：基于SpringBoot2 对 RedisCacheManager 的自定义配置 <br>
     * @see <br>
     * @param redisConnectionFactory
     * @return <br>
     * @RedisCacheManager <br>
     * @methods game.web.config.RedisConfig#cacheManager <br>
     * @author LiBencheng <br>
     * @date Created on 2021年6月3日 <br>
     * @time 下午4:29:58 <br>
     * @version 1.0 <br>
     *************************************************************          <br>
     */
    // @RefreshScope
    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory)
    {
        // 初始化一个RedisCacheWriter
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
        // 设置CacheManager的值序列化方式为json序列化
        RedisSerializer<Object> jsonSerializer = new GenericJackson2JsonRedisSerializer();
        RedisSerializationContext.SerializationPair<Object> pair =
            RedisSerializationContext.SerializationPair.fromSerializer(jsonSerializer);
        RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
        
        // 设置默认超过时期是1天
        defaultCacheConfig.entryTtl(Duration.ofDays(1));
        // 初始化RedisCacheManager
        RedisCacheManager redisCacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
        
        return redisCacheManager;
    }
    
    // //@RefreshScope
    // @Bean
    // public CacheManager cacheManager(RedisTemplate redisTemplate)
    // {
    //
    // // 初始化一个RedisCacheWriter
    // RedisCacheWriter redisCacheWriter =
    // RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
    // // 设置CacheManager的值序列化方式为json序列化
    // RedisSerializer<Object> jsonSerializer = new GenericJackson2JsonRedisSerializer();
    // RedisSerializationContext.SerializationPair<Object> pair =
    // RedisSerializationContext.SerializationPair.fromSerializer(jsonSerializer);
    // RedisCacheConfiguration defaultCacheConfig =
    // RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
    //
    // // 设置默认超过时期是1天
    // defaultCacheConfig.entryTtl(Duration.ofDays(1));
    // // 初始化RedisCacheManager
    // RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
    //
    // // RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
    // // // Number of seconds before expiration. Defaults to unlimited (0)
    // // cacheManager.setDefaultExpiration(10); // 设置key-value超时时间
    // return cacheManager;
    // }
    //
    // @RefreshScope
    @Bean
    public RedisTemplate<String, String> redisTemplateFun(RedisConnectionFactory factory)
    {
        StringRedisTemplate template = new StringRedisTemplate(factory);
        setSerializer(template); // 设置序列化工具，这样ReportBean不需要实现Serializable接口
        template.afterPropertiesSet();
        return template;
    }
    
    @SuppressWarnings("all")
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory)
    {
        
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        
        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        
        template.afterPropertiesSet();
        
        return template;
    }
    
    // @RefreshScope
    private void setSerializer(StringRedisTemplate template)
    {
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setValueSerializer(jackson2JsonRedisSerializer);
    }
    
    @Value("${spring.redis.expiretime}")
    private String expiretime;
    
    @Value("${spring.redis.expiretimeunit}")
    private String expiretimeunit;
    
    private Map<String, Object> expireMap;
    
    public Map<String, Object> getExpireMap()
    {
        expireMap = new LinkedHashMap<String, Object>();
        expireMap.put(EXPIRETIME, Long.valueOf(expiretime));
        expireMap.put(EXPIRETIMEUNIT, TimeUnit.valueOf(expiretimeunit));
        
        return expireMap;
    }
    
    public static String EXPIRETIME = "expiretime";
    public static String EXPIRETIMEUNIT = "expiretimeunit";
}
